/**
 * @description: JWT验证并增加参数
 * @version: 0.0.1
 * @author: xiech
 * @Date: 2023-06-01 14:10:24
 */

import { Config, Inject, Middleware } from '@midwayjs/decorator';
import { Context, NextFunction } from '@midwayjs/koa';
import { httpError, RouterInfo, MidwayWebRouterService } from '@midwayjs/core';
import { JwtService } from '@midwayjs/jwt';

/**
 * 安全验证
 */
@Middleware()
export class JwtMiddleware {
  @Inject()
  jwtUtil: JwtService;

  @Inject()
  webRouterService: MidwayWebRouterService;

  @Inject()
  notLoginRouters: RouterInfo[];

  @Config('middleware.jwt')
  jwtConfig: any;

  resolve() {
    return async (ctx: Context, next: NextFunction) => {
      const routeInfo = await this.webRouterService.getMatchedRouterInfo(ctx.path, ctx.method);

      if (!routeInfo) {
        await next();
        return;
      }
      //  过滤不需要登录的接口
      if (
        this.notLoginRouters.some(
          o =>
            o.requestMethod === routeInfo.requestMethod &&
            o.url === routeInfo.url
        )
      ) {
        await next();
        return;
      }

      if (!ctx.headers['x-morefunoa-access-token']) {
        throw new httpError.UnauthorizedError('缺少凭证');
      }
      const parts = ctx.get('x-morefunoa-access-token').trim().split(' ');
      if (parts.length !== 2) {
        throw new httpError.UnauthorizedError('无效的凭证');
      }
      const [scheme, token] = parts;
      if (!/^Bearer$/i.test(scheme)) {
        throw new httpError.UnauthorizedError('缺少Bearer');
      }
      // 验证token，过期会抛出异常
      // 返回参数 iat 令牌的签发时间 exp 令牌的过期时间
      try {
        const jwt: Object = await this.jwtUtil.verify(token, { complete: false });
        ctx.unionId = jwt['data']['unionId'];
      } catch (error) {
        throw new httpError.UnauthorizedError('token过期，请重新登录');
      }
      return next();
    };
  }

  public match(ctx: Context): boolean {
    const { path } = ctx;
    const { prefix } = this.jwtConfig;
    return path.indexOf(prefix) === 0;
  }

  public static getName(): string {
    return 'Jwt';
  }
}
